<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN">
<head>
  <title></title>
  <link href="../Styles/stylesheet.css" rel="stylesheet" type="text/css" />
  
<style type="text/css">
@page { margin-bottom: 5.000000pt; margin-top: 5.000000pt; }
</style>
</head>

<body>
  <h2><span style="border-bottom:1px solid">Chapter_4</span></h2>

  <p>stem.out.print("0");</p>

  <p>System.out.println();</p>

  <p>}</p>

  <p>static void pBinLong(String s, long l) {</p>

  <p>System.out.println(</p>

  <p>s + ", long: " + l + ", binary: ");</p>

  <p>System.out.print(" ");</p>

  <p>for(int i = 63; i &gt;=0; i--)</p>

  <p>if(((1L &lt;&lt; i) &amp; l) != 0)</p>

  <p>System.out.print("1");</p>

  <p>else</p>

  <p>System.out.print("0");</p>

  <p>System.out.println();</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>程序末尾调用了两个方法：pBinInt()和pBinLong()。它们分别操作一个int和long值，并用一种二进制格式输出，同时附有简要的说明文字。目前，可暂时忽略它们具体的实现方案。</p>

  <p>大家要注意的是System.out.print()的使用，而不是System.out.println()。print()方法不会产生一个新行，以便在同一行里罗列多种信息。</p>

  <p>除展示所有按位运算符针对int和long的效果之外，本例也展示了int和long的最小值、最大值、+1和-1值，使大家能体会它们的情况。注意高位代表正负号：0为正，1为负。下面列出int部分的输出：</p>

  <p>-1, int: -1, binary:</p>

  <p>11111111111111111111111111111111</p>

  <p>+1, int: 1, binary:</p>

  <p>00000000000000000000000000000001</p>

  <p>maxpos, int: 2147483647, binary:</p>

  <p>01111111111111111111111111111111</p>

  <p>maxneg, int: -2147483648, binary:</p>

  <p>10000000000000000000000000000000</p>

  <p>i, int: 59081716, binary:</p>

  <p>00000011100001011000001111110100</p>

  <p>~i, int: -59081717, binary:</p>

  <p>11111100011110100111110000001011</p>

  <p>-i, int: -59081716, binary:</p>

  <p>11111100011110100111110000001100</p>

  <p>j, int: 198850956, binary:</p>

  <p>00001011110110100011100110001100</p>

  <p>i &amp; j, int: 58720644, binary:</p>

  <p>00000011100000000000000110000100</p>

  <p>i | j, int: 199212028, binary:</p>

  <p>00001011110111111011101111111100</p>

  <p>i ^ j, int: 140491384, binary:</p>

  <p>00001000010111111011101001111000</p>

  <p>i &lt;&lt; 5, int: 1890614912, binary:</p>

  <p>01110000101100000111111010000000</p>

  <p>i &gt;&gt; 5, int: 1846303, binary:</p>

  <p>00000000000111000010110000011111</p>

  <p>(~i) &gt;&gt; 5, int: -1846304, binary:</p>

  <p>11111111111000111101001111100000</p>

  <p>i &gt;&gt;&gt; 5, int: 1846303, binary:</p>

  <p>00000000000111000010110000011111</p>

  <p>(~i) &gt;&gt;&gt; 5, int: 132371424, binary:</p>

  <p>00000111111000111101001111100000</p>

  <p>数字的二进制形式表现为“有符号2的补值”。</p>

  <p>3.1.9 三元if-else运算符</p>

  <p>这种运算符比较罕见，因为它有三个运算对象。但它确实属于运算符的一种，因为它最终也会生成一个值。这与本章后一节要讲述的普通if-else语句是不同的。表达式采取下述形式：</p>

  <p>布尔表达式 ? 值0:值1</p>

  <p>若“布尔表达式”的结果为true，就计算“值0”，而且它的结果成为最终由运算符产生的值。但若“布尔表达式”的结果为false，计算的就是“值1”，而且它的结果成为最终由运算符产生的值。</p>

  <p>当然，也可以换用普通的if-else语句（在后面介绍），但三元运算符更加简洁。尽管C引以为傲的就是它是一种简练的语言，而且三元运算符的引入多半就是为了体现这种高效率的编程，但假若您打算频繁用它，还是要先多作一些思量――它很容易就会产生可读性极差的代码。</p>

  <p>可将条件运算符用于自己的“副作用”，或用于它生成的值。但通常都应将其用于值，因为那样做可将运算符与if-else明确区别开。下面便是一个例子：</p>

  <p>static int ternary(int i) {</p>

  <p>return i &lt; 10 ? i * 100 : i * 10;</p>

  <p>}</p>

  <p>可以看出，假设用普通的if-else结构写上述代码，代码量会比上面多出许多。如下所示：</p>

  <p>static int alternative(int i) {</p>

  <p>if (i &lt; 10)</p>

  <p>return i * 100;</p>

  <p>return i * 10;</p>

  <p>}</p>

  <p>但第二种形式更易理解，而且不要求更多的录入。所以在挑选三元运算符时，请务必权衡一下利弊。</p>

  <p>3.1.10 逗号运算符</p>

  <p>在C和C++里，逗号不仅作为函数自变量列表的分隔符使用，也作为进行后续计算的一个运算符使用。在Java里需要用到逗号的唯一场所就是for循环，本章稍后会对此详加解释。</p>

  <p>3.1.11 字串运算符+</p>

  <p>这个运算符在Java里有一项特殊用途：连接不同的字串。这一点已在前面的例子中展示过了。尽管与+的传统意义不符，但用+来做这件事情仍然是非常自然的。在C++里，这一功能看起来非常不错，所以引入了一项“运算符过载”机制，以便C++程序员为几乎所有运算符增加特殊的含义。但非常不幸，与C++的另外一些限制结合，运算符过载成为一种非常复杂的特性，程序员在设计自己的类时必须对此有周到的考虑。与C++相比，尽管运算符过载在Java里更易实现，但迄今为止仍然认为这一特性过于复杂。所以Java程序员不能象C++程序员那样设计自己的过载运算符。</p>

  <p>我们注意到运用“String +”时一些有趣的现象。若表达式以一个String起头，那么后续所有运算对象都必须是字串。如下所示：</p>

  <p>int x = 0, y = 1, z = 2;</p>

  <p>String sString = "x, y, z ";</p>

  <p>System.out.println(sString + x + y + z);</p>

  <p>在这里，Java编译程序会将x，y和z转换成它们的字串形式，而不是先把它们加到一起。然而，如果使用下述语句：</p>

  <p>System.out.println(x + sString);</p>

  <p>那么早期版本的Java就会提示出错（以后的版本能将x转换成一个字串）。因此，如果想通过“加号”连接字串（使用Java的早期版本），请务必保证第一个元素是字串（或加上引号的一系列字符，编译能将其识别成一个字串）。</p>

  <p>3.1.12 运算符常规操作规则</p>

  <p>使用运算符的一个缺点是括号的运用经常容易搞错。即使对一个表达式如何计算有丝毫不确定的因素，都容易混淆括号的用法。这个问题在Java里仍然存在。</p>

  <p>在C和C++中，一个特别常见的错误如下：</p>

  <p>while(x = y) {</p>

  <p>//...</p>

  <p>}</p>

  <p>程序的意图是测试是否“相等”（==），而不是进行赋值操作。在C和C++中，若y是一个非零值，那么这种赋值的结果肯定是true。这样使可能得到一个无限循环。在Java里，这个表达式的结果并不是布尔值，而编译器期望的是一个布尔值，而且不会从一个int数值中转换得来。所以在编译时，系统就会提示出现错误，有效地阻止我们进一步运行程序。所以这个缺点在Java里永远不会造成更严重的后果。唯一不会得到编译错误的时候是x和y都为布尔值。在这种情况下，x = y属于合法表达式。而在上述情况下，则可能是一个错误。</p>

  <p>在C和C++里，类似的一个问题是使用按位AND和OR，而不是逻辑AND和OR。按位AND和OR使用两个字符之一（&amp;或|），而逻辑AND和OR使用两个相同的字符（&amp;&amp;或||）。就象“=”和“==”一样，键入一个字符当然要比键入两个简单。在Java里，编译器同样可防止这一点，因为它不允许我们强行使用一种并不属于的类型。</p>

  <p>3.1.13 造型运算符</p>

  <p>“造型”（Cast）的作用是“与一个模型匹配”。在适当的时候，Java会将一种数据类型自动转换成另一种。例如，假设我们为浮点变量分配一个整数值，计算机会将int自动转换成float。通过造型，我们可明确设置这种类型的转换，或者在一般没有可能进行的时候强迫它进行。</p>

  <p>为进行一次造型，要将括号中希望的数据类型（包括所有修改符）置于其他任何值的左侧。下面是一个例子：</p>

  <p>void casts() {</p>

  <p>int i = 200;</p>

  <p>long l = (long)i;</p>

  <p>long l2 = (long)200;</p>

  <p>}</p>

  <p>正如您看到的那样，既可对一个数值进行造型处理，亦可对一个变量进行造型处理。但在这儿展示的两种情况下，造型均是多余的，因为编译器在必要的时候会自动进行int值到long值的转换。当然，仍然可以设置一个造型，提醒自己留意，也使程序更清楚。在其他情况下，造型只有在代码编译时才显出重要性。</p>

  <p>在C和C++中，造型有时会让人头痛。在Java里，造型则是一种比较安全的操作。但是，若进行一种名为“缩小转换”（Narrowing Conversion）的操作（也就是说，脚本是能容纳更多信息的数据类型，将其转换成容量较小的类型），此时就可能面临信息丢失的危险。此时，编译器会强迫我们进行造型，就好象说：“这可能是一件危险的事情――如果您想让我不顾一切地做，那么对不起，请明确造型。”而对于“放大转换”（Widening conversion），则不必进行明确造型，因为新类型肯定能容纳原来类型的信息，不会造成任何信息的丢失。</p>

  <p>Java允许我们将任何主类型“造型”为其他任何一种主类型，但布尔值（bollean）要除外，后者根本不允许进行任何造型处理。“类”不允许进行造型。为了将一种类转换成另一种，必须采用特殊的方法（字串是一种特殊的情况，本书后面会讲到将对象造型到一个类型“家族”里；例如，“橡树”可造型为“树”；反之亦然。但对于其他外来类型，如“岩石”，则不能造型为“树”）。</p>

  <p>1. 字面值</p>

  <p>最开始的时候，若在一个程序里插入“字面值”（Literal），编译器通常能准确知道要生成什么样的类型。但在有些时候，对于类型却是暧昧不清的。若发生这种情况，必须对编译器加以适当的“指导”。方法是用与字面值关联的字符形式加入一些额外的信息。下面这段代码向大家展示了这些字符。</p>

  <p>//: Literals.java</p>

  <p>class Literals {</p>

  <p>char c = 0xffff; // max char hex value</p>

  <p>byte b = 0x7f; // max byte hex value</p>

  <p>short s = 0x7fff; // max short hex value</p>

  <p>int i1 = 0x2f; // Hexadecimal (lowercase)</p>

  <p>int i2 = 0X2F; // Hexadecimal (uppercase)</p>

  <p>int i3 = 0177; // Octal (leading zero)</p>

  <p>// Hex and Oct also work with long.</p>

  <p>long n1 = 200L; // long suffix</p>

  <p>long n2 = 200l; // long suffix</p>

  <p>long n3 = 200;</p>

  <p>//! long l6(200); // not allowed</p>

  <p>float f1 = 1;</p>

  <p>float f2 = 1F; // float suffix</p>

  <p>float f3 = 1f; // float suffix</p>

  <p>float f4 = 1e-45f; // 10 to the power</p>

  <p>float f5 = 1e+9f; // float suffix</p>

  <p>double d1 = 1d; // double suffix</p>

  <p>double d2 = 1D; // double suffix</p>

  <p>double d3 = 47e47d; // 10 to the power</p>

  <p>} ///:~</p>

  <p>十六进制（Base 16）――它适用于所有整数数据类型――用一个前置的0x或0X指示。并在后面跟随采用大写或小写形式的0-9以及a-f。若试图将一个变量初始化成超出自身能力的一个值（无论这个值的数值形式如何），编译器就会向我们报告一条出错消息。注意在上述代码中，最大的十六进制值只会在char，byte以及short身上出现。若超出这一限制，编译器会将值自动变成一个int，并告诉我们需要对这一次赋值进行“缩小造型”。这样一来，我们就可清楚获知自己已超载了边界。</p>

  <p>八进制（Base 8）是用数字中的一个前置0以及0-7的数位指示的。在C，C++或者Java中，对二进制数字没有相应的“字面”表示方法。</p>

  <p>字面值后的尾随字符标志着它的类型。若为大写或小写的L，代表long；大写或小写的F，代表float；大写或小写的D，则代表double。</p>

  <p>指数总是采用一种我们认为很不直观的记号方法：1.39e-47f。在科学与工程学领域，“e”代表自然对数的基数，约等于2.718（Java一种更精确的double值采用Math.E的形式）。它在象“1.39×e的-47次方”这样的指数表达式中使用，意味着“1.39×2.718的-47次方”。然而，自FORTRAN语言发明后，人们自然而然地觉得e代表“10多少次幂”。这种做法显得颇为古怪，因为FORTRAN最初面向的是科学与工程设计领域。理所当然，它的设计者应对这样的混淆概念持谨慎态度（注释①）。但不管怎样，这种特别的表达方法在C，C++以及现在的Java中顽固地保留下来了。所以倘若您习惯将e作为自然对数的基数使用，那么在Java中看到象“1.39e-47f”这样的表达式时，请转换您的思维，从程序设计的角度思考它；它真正的含义是“1.39×10的-47次方”。</p>

  <p>①：John Kirkham这样写道：“我最早于1962年在一部IBM 1620机器上使用FORTRAN II。那时――包括60年代以及70年代的早期，FORTRAN一直都是使用大写字母。之所以会出现这一情况，可能是由于早期的输入设备大多是老式电传打字机，使用5位Baudot码，那种码并不具备小写能力。乘幂表达式中的‘E’也肯定是大写的，所以不会与自然对数的基数‘e’发生冲突，后者必然是小写的。‘E’这个字母的含义其实很简单，就是‘Exponential’的意思，即‘指数’或‘幂数’，代表计算系统的基数――一般都是10。当时，八进制也在程序员中广泛使用。尽管我自己未看到它的使用，但假若我在乘幂表达式中看到一个八进制数字，就会把它认作Base 8。我记得第一次看到用小写‘e’表示指数是在70年代末期。我当时也觉得它极易产生混淆。所以说，这个问题完全是自己‘潜入’FORTRAN里去的，并非一开始就有。如果你真的想使用自然对数的基数，实际有现成的函数可供利用，但它们都是大写的。”</p>

  <p>注意如果编译器能够正确地识别类型，就不必使用尾随字符。对于下述语句：</p>

  <p>long n3 = 200;</p>

  <p>它并不存在含混不清的地方，所以200后面的一个L大可省去。然而，对于下述语句：</p>

  <p>float f4 = 1e-47f; //10的幂数</p>

  <p>编译器通常会将指数作为双精度数（double）处理，所以假如没有这个尾随的f，就会收到一条出错提示，告诉我们须用一个“造型”将double转换成float。</p>

  <p>2. 转型</p>

  <p>大家会发现假若对主数据类型执行任何算术或按位运算，只要它们“比int小”（即char，byte或者short），那么在正式执行运算之前，那些值会自动转换成int。这样一来，最终生成的值就是int类型。所以只要把一个值赋回较小的类型，就必须使用“造型”。此外，由于是将值赋回给较小的类型，所以可能出现信息丢失的情况）。通常，表达式中最大的数据类型是决定了表达式最终结果大小的那个类型。若将一个float值与一个double值相乘，结果就是double；如将一个int和一个long值相加，则结果为long。</p>

  <p>3.1.14 Java没有“sizeof”</p>

  <p>在C和C++中，sizeof()运算符能满足我们的一项特殊需要：获知为数据项目分配的字符数量。在C和C++中，size()最常见的一种应用就是“移植”。不同的数据在不同的机器上可能有不同的大小，所以在进行一些对大小敏感的运算时，程序员必须对那些类型有多大做到心中有数。例如，一台计算机可用32位来保存整数，而另一台只用16位保存。显然，在第一台机器中，程序可保存更大的值。正如您可能已经想到的那样，移植是令C和C++程序员颇为头痛的一个问题。</p>

  <p>Java不需要sizeof()运算符来满足这方面的需要，因为所有数据类型在所有机器的大小都是相同的。我们不必考虑移植问题――Java本身就是一种“与平台无关”的语言。</p>

  <p>3.1.15 复习计算顺序</p>

  <p>在我举办的一次培训班中，有人抱怨运算符的优先顺序太难记了。一名学生推荐用一句话来帮助记忆：“Ulcer Addicts Really Like C A lot”，即“溃疡患者特别喜欢（维生素）C”。</p>

  <p>助记词</p>

  <p>运算符类型</p>

  <p>运算符</p>

  <p>Ulcer</p>

  <p>Unary</p>

  <p>+ - ++ [[ rest...]]</p>

  <p>Addicts</p>

  <p>Arithmetic (and shift)</p>

  <p>* / % + - &lt;&lt; &gt;&gt;</p>

  <p>Really</p>

  <p>Relational</p>

  <p>&gt; &lt; &gt;= &lt;= == !=</p>

  <p>Like</p>

  <p>Logical (and bitwise)</p>

  <p>&amp;&amp; || &amp; | ^</p>

  <p>C</p>

  <p>Conditional (ternary)</p>

  <p>A &gt; B ? X : Y</p>

  <p>A Lot</p>

  <p>Assignment</p>

  <p>= (and compound assignment like *=)当然，对于移位和按位运算符，上表并不是完美的助记方法；但对于其他运算来说，它确实很管用。</p>

  <p>3.1.16 运算符总结</p>

  <p>下面这个例子向大家展示了如何随同特定的运算符使用主数据类型。从根本上说，它是同一个例子反反复复地执行，只是使用了不同的主数据类型。文件编译时不会报错，因为那些会导致错误的行已用//!变成了注释内容。</p>

  <p>//: AllOps.java</p>

  <p>// Tests all the operators on all the</p>

  <p>// primitive data types to show which</p>

  <p>// ones are accepted by the Java compiler.</p>

  <p>class AllOps {</p>

  <p>// To accept the results of a boolean test:</p>

  <p>void f(boolean b) {}</p>

  <p>void boolTest(boolean x, boolean y) {</p>

  <p>// Arithmetic operators:</p>

  <p>//! x = x * y;</p>

  <p>//! x = x / y;</p>

  <p>//! x = x % y;</p>

  <p>//! x = x + y;</p>

  <p>//! x = x - y;</p>

  <p>//! x++;</p>

  <p>//! x--;</p>

  <p>//! x = +y;</p>

  <p>//! x = -y;</p>

  <p>// Relational and logical:</p>

  <p>//! f(x &gt; y);</p>

  <p>//! f(x &gt;= y);</p>

  <p>//! f(x &lt; y);</p>

  <p>//! f(x &lt;= y);</p>

  <p>f(x == y);</p>

  <p>f(x != y);</p>

  <p>f(!y);</p>

  <p>x = x &amp;&amp; y;</p>

  <p>x = x || y;</p>

  <p>// Bitwise operators:</p>

  <p>//! x = ~y;</p>

  <p>x = x &amp; y;</p>

  <p>x = x | y;</p>

  <p>x = x ^ y;</p>

  <p>//! x = x &lt;&lt; 1;</p>

  <p>//! x = x &gt;&gt; 1;</p>

  <p>//! x = x &gt;&gt;&gt; 1;</p>

  <p>// Compound assignment:</p>

  <p>//! x += y;</p>

  <p>//! x -= y;</p>

  <p>//! x *= y;</p>

  <p>//! x /= y;</p>

  <p>//! x %= y;</p>

  <p>//! x &lt;&lt;= 1;</p>

  <p>//! x &gt;&gt;= 1;</p>

  <p>//! x &gt;&gt;&gt;= 1;</p>

  <p>x &amp;= y;</p>

  <p>x ^= y;</p>

  <p>x |= y;</p>

  <p>// Casting:</p>

  <p>//! char c = (char)x;</p>

  <p>//! byte B = (byte)x;</p>

  <p>//! short s = (short)x;</p>

  <p>//! int i = (int)x;</p>

  <p>//! long l = (long)x;</p>

  <p>//! float f = (float)x;</p>

  <p>//! double d = (double)x;</p>

  <p>}</p>

  <p>void charTest(char x, char y) {</p>

  <p>// Arithmetic operators:</p>

  <p>x = (char)(x * y);</p>

  <p>x = (char)(x / y);</p>

  <p>x = (char)(x % y);</p>

  <p>x = (char)(x + y);</p>

  <p>x = (char)(x - y);</p>

  <p>x++;</p>

  <p>x--;</p>

  <p>x = (char)+y;</p>

  <p>x = (char)-y;</p>

  <p>// Relational and logical:</p>

  <p>f(x &gt; y);</p>

  <p>f(x &gt;= y);</p>

  <p>f(x &lt; y);</p>

  <p>f(x &lt;= y);</p>

  <p>f(x == y);</p>

  <p>f(x != y);</p>

  <p>//! f(!x);</p>

  <p>//! f(x &amp;&amp; y);</p>

  <p>//! f(x || y);</p>

  <p>// Bitwise operators:</p>

  <p>x= (char)~y;</p>

  <p>x = (char)(x &amp; y);</p>

  <p>x = (char)(x | y);</p>

  <p>x = (char)(x ^ y);</p>

  <p>x = (char)(x &lt;&lt; 1);</p>

  <p>x = (char)(x &gt;&gt; 1);</p>

  <p>x = (char)(x &gt;&gt;&gt; 1);</p>

  <p>// Compound assignment:</p>

  <p>x += y;</p>

  <p>x -= y;</p>

  <p>x *= y;</p>

  <p>x /= y;</p>

  <p>x %= y;</p>

  <p>x &lt;&lt;= 1;</p>

  <p>x &gt;&gt;= 1;</p>

  <p>x &gt;&gt;&gt;= 1;</p>

  <p>x &amp;= y;</p>

  <p>x ^= y;</p>

  <p>x |= y;</p>

  <p>// Casting:</p>

  <p>//! boolean b = (boolean)x;</p>

  <p>byte B = (byte)x;</p>

  <p>short s = (short)x;</p>

  <p>int i = (int)x;</p>

  <p>long l = (long)x;</p>

  <p>float f = (float)x;</p>

  <p>double d = (double)x;</p>

  <p>}</p>

  <p>void byteTest(byte x, byte y) {</p>

  <p>// Arithmetic operators:</p>

  <p>x = (byte)(x* y);</p>

  <p>x = (byte)(x / y);</p>

  <p>x = (byte)(x % y);</p>

  <p>x = (byte)(x + y);</p>

  <p>x = (byte)(x - y);</p>

  <p>x++;</p>

  <p>x--;</p>

  <p>x = (byte)+ y;</p>

  <p>x = (byte)- y;</p>

  <p>// Relational and logical:</p>

  <p>f(x &gt; y);</p>

  <p>f(x &gt;= y);</p>

  <p>f(x &lt; y);</p>

  <p>f(x &lt;= y);</p>

  <p>f(x == y);</p>

  <p>f(x != y);</p>

  <p>//! f(!x);</p>

  <p>//! f(x &amp;&amp; y);</p>

  <p>//! f(x || y);</p>

  <p>// Bitwise operators:</p>

  <p>x = (byte)~y;</p>

  <p>x = (byte)(x &amp; y);</p>

  <p>x = (byte)(x | y);</p>

  <p>x = (byte)(x ^ y);</p>

  <p>x = (byte)(x &lt;&lt; 1);</p>

  <p>x = (byte)(x &gt;&gt; 1);</p>

  <p>x = (byte)(x &gt;&gt;&gt; 1);</p>

  <p>// Compound assignment:</p>

  <p>x += y;</p>

  <p>x -= y;</p>

  <p>x *= y;</p>

  <p>x /= y;</p>

  <p>x %= y;</p>

  <p>x &lt;&lt;= 1;</p>

  <p>x &gt;&gt;= 1;</p>

  <p>x &gt;&gt;&gt;= 1;</p>

  <p>x &amp;= y;</p>

  <p>x ^= y;</p>

  <p>x |= y;</p>

  <p>// Casting:</p>

  <p>//! boolean b = (boolean)x;</p>

  <p>char c = (char)x;</p>

  <p>short s = (short)x;</p>

  <p>int i = (int)x;</p>

  <p>long l = (long)x;</p>

  <p>float f = (float)x;</p>

  <p>double d = (double)x;</p>

  <p>}</p>

  <p>void shortTest(short x, short y) {</p>

  <p>// Arithmetic operators:</p>

  <p>x = (short)(x * y);</p>

  <p>x = (short)(x / y);</p>

  <p>x = (short)(x % y);</p>

  <p>x = (short)(x + y);</p>

  <p>x = (short)(x - y);</p>

  <p>x++;</p>

  <p>x--;</p>

  <p>x = (short)+y;</p>

  <p>x = (short)-y;</p>

  <p>// Relational and logical:</p>

  <p>f(x &gt; y);</p>

  <p>f(x &gt;= y);</p>

  <p>f(x &lt; y);</p>

  <p>f(x &lt;= y);</p>

  <p>f(x == y);</p>

  <p>f(x != y);</p>

  <p>//! f(!x);</p>

  <p>//! f(x &amp;&amp; y);</p>

  <p>//! f(x || y);</p>

  <p>// Bitwise operators:</p>

  <p>x = (short)~y;</p>

  <p>x = (short)(x &amp; y);</p>

  <p>x = (short)(x | y);</p>

  <p>x = (short)(x ^ y);</p>

  <p>x = (short)(x &lt;&lt; 1);</p>

  <p>x = (short)(x &gt;&gt; 1);</p>

  <p>x = (short)(x &gt;&gt;&gt; 1);</p>

  <p>// Compound assignment:</p>

  <p>x += y;</p>

  <p>x -= y;</p>

  <p>x *= y;</p>

  <p>x /= y;</p>

  <p>x %= y;</p>

  <p>x &lt;&lt;= 1;</p>

  <p>x &gt;&gt;= 1;</p>

  <p>x &gt;&gt;&gt;= 1;</p>

  <p>x &amp;= y;</p>

  <p>x ^= y;</p>

  <p>x |= y;</p>

  <p>// Casting:</p>

  <p>//! boolean b = (boolean)x;</p>

  <p>char c = (char)x;</p>

  <p>byte B = (byte)x;</p>

  <p>int i = (int)x;</p>

  <p>long l = (long)x;</p>

  <p>float f = (float)x;</p>

  <p>double d = (double)x;</p>

  <p>}</p>

  <p>void intTest(int x, int y) {</p>

  <p>// Arithmetic operators:</p>

  <p>x = x * y;</p>

  <p>x = x / y;</p>

  <p>x = x % y;</p>

  <p>x = x + y;</p>

  <p>x = x - y;</p>

  <p>x++;</p>

  <p>x--;</p>

  <p>x = +y;</p>

  <p>x = -y;</p>

  <p>// Relational and logical:</p>

  <p>f(x &gt; y);</p>

  <p>f(x &gt;= y);</p>

  <p>f(x &lt; y);</p>

  <p>f(x &lt;= y);</p>

  <p>f(x == y);</p>

  <p>f(x != y);</p>

  <p>//! f(!x);</p>

  <p>//! f(x &amp;&amp; y);</p>

  <p>//! f(x || y);</p>

  <p>// Bitwise operators:</p>

  <p>x = ~y;</p>

  <p>x = x &amp; y;</p>

  <p>x = x | y;</p>

  <p>x = x ^ y;</p>

  <p>x = x &lt;&lt; 1;</p>

  <p>x = x &gt;&gt; 1;</p>

  <p>x = x &gt;&gt;&gt; 1;</p>

  <p>// Compound assignment:</p>

  <p>x += y;</p>

  <p>x -= y;</p>

  <p>x *= y;</p>

  <p>x /= y;</p>

  <p>x %= y;</p>

  <p>x &lt;&lt;= 1;</p>

  <p>x &gt;&gt;= 1;</p>

  <p>x &gt;&gt;&gt;= 1;</p>

  <p>x &amp;= y;</p>

  <p>x ^= y;</p>

  <p>x |= y;</p>

  <p>// Casting:</p>

  <p>//! boolean b = (boolean)x;</p>

  <p>char c = (char)x;</p>

  <p>byte B = (byte)x;</p>

  <p>short s = (short)x;</p>

  <p>long l = (long)x;</p>

  <p>float f = (float)x;</p>

  <p>double d = (double)x;</p>

  <p>}</p>

  <p>void longTest(long x, long y) {</p>

  <p>// Arithmetic operators:</p>

  <p>x = x * y;</p>

  <p>x = x / y;</p>

  <p>x = x % y;</p>

  <p>x = x + y;</p>

  <p>x = x - y;</p>

  <p>x++;</p>

  <p>x--;</p>

  <p>x = +y;</p>

  <p>x = -y;</p>

  <p>// Relational and logical:</p>

  <p>f(x &gt; y);</p>

  <p>f(x &gt;= y);</p>

  <p>f(x &lt; y);</p>

  <p>f(x &lt;= y);</p>

  <p>f(x == y);</p>

  <p>f(x != y);</p>

  <p>//! f(!x);</p>

  <p>//! f(x &amp;&amp; y);</p>

  <p>//! f(x || y);</p>

  <p>// Bitwise operators:</p>

  <p>x = ~y;</p>

  <p>x = x &amp; y;</p>

  <p>x = x | y;</p>

  <p>x = x ^ y;</p>

  <p>x = x &lt;&lt; 1;</p>

  <p>x = x &gt;&gt; 1;</p>

  <p>x = x &gt;&gt;&gt; 1;</p>

  <p>// Compound assignment:</p>

  <p>x += y;</p>

  <p>x -= y;</p>

  <p>x *= y;</p>

  <p>x /= y;</p>

  <p>x %= y;</p>

  <p>x &lt;&lt;= 1;</p>

  <p>x &gt;&gt;= 1;</p>

  <p>x &gt;&gt;&gt;= 1;</p>

  <p>x &amp;= y;</p>

  <p>x ^= y;</p>

  <p>x |= y;</p>

  <p>// Casting:</p>

  <p>//! boolean b = (boolean)x;</p>

  <p>char c = (char)x;</p>

  <p>byte B = (byte)x;</p>

  <p>short s = (short)x;</p>

  <p>int i = (int)x;</p>

  <p>float f = (float)x;</p>

  <p>double d = (double)x;</p>

  <p>}</p>

  <p>void floatTest(float x, float y) {</p>

  <p>// Arithmetic operators:</p>

  <p>x = x * y;</p>

  <p>x = x / y;</p>

  <p>x = x % y;</p>

  <p>x = x + y;</p>

  <p>x = x - y;</p>

  <p>x++;</p>

  <p>x--;</p>

  <p>x = +y;</p>

  <p>x = -y;</p>

  <p>// Relational and logical:</p>

  <p>f(x &gt; y);</p>

  <p>f(x &gt;= y);</p>

  <p>f(x &lt; y);</p>

  <p>f(x &lt;= y);</p>

  <p>f(x == y);</p>

  <p>f(x != y);</p>

  <p>//! f(!x);</p>

  <p>//! f(x &amp;&amp; y);</p>

  <p>//! f(x || y);</p>

  <p>// Bitwise operators:</p>

  <p>//! x = ~y;</p>

  <p>//! x = x &amp; y;</p>

  <p>//! x = x | y;</p>

  <p>//! x = x ^ y;</p>

  <p>//! x = x &lt;&lt; 1;</p>

  <p>//! x = x &gt;&gt; 1;</p>

  <p>//! x = x &gt;&gt;&gt; 1;</p>

  <p>// Compound assignment:</p>

  <p>x += y;</p>

  <p>x -= y;</p>

  <p>x *= y;</p>

  <p>x /= y;</p>

  <p>x %= y;</p>

  <p>//! x &lt;&lt;= 1;</p>

  <p>//! x &gt;&gt;= 1;</p>

  <p>//! x &gt;&gt;&gt;= 1;</p>

  <p>//! x &amp;= y;</p>

  <p>//! x ^= y;</p>

  <p>//! x |= y;</p>

  <p>// Casting:</p>

  <p>//! boolean b = (boolean)x;</p>

  <p>char c = (char)x;</p>

  <p>byte B = (byte)x;</p>

  <p>short s = (short)x;</p>

  <p>int i = (int)x;</p>

  <p>long l = (long)x;</p>

  <p>double d = (double)x;</p>

  <p>}</p>

  <p>void doubleTest(double x, double y) {</p>

  <p>// Arithmetic operators:</p>

  <p>x = x * y;</p>

  <p>x = x / y;</p>

  <p>x = x % y;</p>

  <p>x = x + y;</p>

  <p>x = x - y;</p>

  <p>x++;</p>

  <p>x--;</p>

  <p>x = +y;</p>

  <p>x = -y;</p>

  <p>// Relational and logical:</p>

  <p>f(x &gt; y);</p>

  <p>f(x &gt;= y);</p>

  <p>f(x &lt; y);</p>

  <p>f(x &lt;= y);</p>

  <p>f(x == y);</p>

  <p>f(x != y);</p>

  <p>//! f(!x);</p>

  <p>//! f(x &amp;&amp; y);</p>

  <p>//! f(x || y);</p>

  <p>// Bitwise operators:</p>

  <p>//! x = ~y;</p>

  <p>//! x = x &amp; y;</p>

  <p>//! x = x | y;</p>

  <p>//! x = x ^ y;</p>

  <p>//! x = x &lt;&lt; 1;</p>

  <p>//! x = x &gt;&gt; 1;</p>

  <p>//! x = x &gt;&gt;&gt; 1;</p>

  <p>// Compound assignment:</p>

  <p>x += y;</p>

  <p>x -= y;</p>

  <p>x *= y;</p>

  <p>x /= y;</p>

  <p>x %= y;</p>

  <p>//! x &lt;&lt;= 1;</p>

  <p>//! x &gt;&gt;= 1;</p>

  <p>//! x &gt;&gt;&gt;= 1;</p>

  <p>//! x &amp;= y;</p>

  <p>//! x ^= y;</p>

  <p>//! x |= y;</p>

  <p>// Casting:</p>

  <p>//! boolean b = (boolean)x;</p>

  <p>char c = (char)x;</p>

  <p>byte B = (byte)x;</p>

  <p>short s = (short)x;</p>

  <p>int i = (int)x;</p>

  <p>long l = (long)x;</p>

  <p>float f = (float)x;</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>注意布尔值（boolean）的能力非常有限。我们只能为其赋予true和false值。而且可测试它为真还是为假，但不可为它们再添加布尔值，或进行其他其他任何类型运算。</p>

  <p>在char，byte和short中，我们可看到算术运算符的“转型”效果。对这些类型的任何一个进行算术运算，都会获得一个int结果。必须将其明确“造型”回原来的类型（缩小转换会造成信息的丢失），以便将值赋回那个类型。但对于int值，却不必进行造型处理，因为所有数据都已经属于int类型。然而，不要放松警惕，认为一切事情都是安全的。如果对两个足够大的int值执行乘法运算，结果值就会溢出。下面这个例子向大家展示了这一点：</p>

  <p>//: Overflow.java</p>

  <p>// Surprise! Java lets you overflow.</p>

  <p>public class Overflow {</p>

  <p>public static void main(String[] args) {</p>

  <p>int big = 0x7fffffff; // max int value</p>

  <p>prt("big = " + big);</p>

  <p>int bigger = big * 4;</p>

  <p>prt("bigger = " + bigger);</p>

  <p>}</p>

  <p>static void prt(String s) {</p>

  <p>System.out.println(s);</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>输出结果如下：</p>

  <p>big = 2147483647</p>

  <p>bigger = -4</p>

  <p>而且不会从编译器那里收到出错提示，运行时也不会出现异常反应。爪哇咖啡（Java）确实是很好的东西，但却没有“那么”好！</p>

  <p>对于char，byte或者short，混合赋值并不需要造型。即使它们执行转型操作，也会获得与直接算术运算相同的结果。而在另一方面，将造型略去可使代码显得更加简练。</p>

  <p>大家可以看到，除boolean以外，任何一种主类型都可通过造型变为其他主类型。同样地，当造型成一种较小的类型时，必须留意“缩小转换”的后果。否则会在造型过程中不知不觉地丢失信息。</p>

  <p>3.2 执行控制</p>

  <p>Java使用了C的全部控制语句，所以假期您以前用C或C++编程，其中大多数都应是非常熟悉的。大多数程序化的编程语言都提供了某种形式的控制语句，这在语言间通常是共通的。在Java里，涉及的关键字包括if-else、while、do-while、for以及一个名为switch的选择语句。然而，Java并不支持非常有害的goto（它仍是解决某些特殊问题的权宜之计）。仍然可以进行象goto那样的跳转，但比典型的goto要局限多了。</p>

  <p>3.2.1 真和假</p>

  <p>所有条件语句都利用条件表达式的真或假来决定执行流程。条件表达式的一个例子是A==B。它用条件运算符“==”来判断A值是否等于B值。该表达式返回true或false。本章早些时候接触到的所有关系运算符都可拿来构造一个条件语句。注意Java不允许我们将一个数字作为布尔值使用，即使它在C和C++里是允许的（真是非零，而假是零）。若想在一次布尔测试中使用一个非布尔值――比如在if(a)里，那么首先必须用一个条件表达式将其转换成一个布尔值，例如if(a!=0)。</p>

  <p>3.2.2 if-else</p>

  <p>if-else语句或许是控制程序流程最基本的形式。其中的else是可选的，所以可按下述两种形式来使用if：</p>

  <p>if(布尔表达式)</p>

  <p>语句</p>

  <p>或者</p>

  <p>if(布尔表达式)</p>

  <p>语句</p>

  <p>else</p>

  <p>语句</p>

  <p>条件必须产生一个布尔结果。“语句”要么是用分号结尾的一个简单语句，要么是一个复合语句――封闭在括号内的一组简单语句。在本书任何地方，只要提及“语句”这个词，就有可能包括简单或复合语句。</p>

  <p>作为if-else的一个例子，下面这个test()方法可告诉我们猜测的一个数字位于目标数字之上、之下还是相等：</p>

  <p>static int test(int testval) {</p>

  <p>int result = 0;</p>

  <p>if(testval &gt; target)</p>

  <p>result = -1;</p>

  <p>else if(testval &lt; target)</p>

  <p>result = +1;</p>

  <p>else</p>

  <p>result = 0; // match</p>

  <p>return result;</p>

  <p>}</p>

  <p>最好将流程控制语句缩进排列，使读者能方便地看出起点与终点。</p>

  <p>1. return</p>

  <p>return关键字有两方面的用途：指定一个方法返回什么值（假设它没有void返回值），并立即返回那个值。可据此改写上面的test()方法，使其利用这些特点：</p>

  <p>static int test2(int testval) {</p>

  <p>if(testval &gt; target)</p>

  <p>return -1;</p>

  <p>if(testval &lt; target)</p>

  <p>return +1;</p>

  <p>return 0; // match</p>

  <p>}</p>

  <p>不必加上else，因为方法在遇到return后便不再继续。</p>

  <p>3.2.3 反复</p>

  <p>while，do-while和for控制着循环，有时将其划分为“反复语句”。除非用于控制反复的布尔表达式得到“假”的结果，否则语句会重复执行下去。while循环的格式如下：</p>

  <p>while(布尔表达式)</p>

  <p>语句</p>

  <p>在循环刚开始时，会计算一次“布尔表达式”的值。而对于后来每一次额外的循环，都会在开始前重新计算一次。</p>

  <p>下面这个简单的例子可产生随机数，直到符合特定的条件为止：</p>

  <p>//: WhileTest.java</p>

  <p>// Demonstrates the while loop</p>

  <p>public class WhileTest {</p>

  <p>public static void main(String[] args) {</p>

  <p>double r = 0;</p>

  <p>while(r &lt; 0.99d) {</p>

  <p>r = Math.random();</p>

  <p>System.out.println(r);</p>

  <p>}</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>它用到了Math库里的static（静态）方法random()。该方法的作用是产生0和1之间（包括0，但不包括1）的一个double值。while的条件表达式意思是说：“一直循环下去，直到数字等于或大于0.99”。由于它的随机性，每运行一次这个程序，都会获得大小不同的数字列表。</p>

  <p>3.2.4 do-while</p>

  <p>do-while的格式如下：</p>

  <p>do</p>

  <p>语句</p>

  <p>while(布尔表达式)</p>

  <p>while和do-while唯一的区别就是do-while肯定会至少执行一次；也就是说，至少会将其中的语句“过一遍”――即便表达式第一次便计算为false。而在while循环结构中，若条件第一次就为false，那么其中的语句根本不会执行。在实际应用中，while比do-while更常用一些。</p>

  <p>3.2.5 for</p>

  <p>for循环在第一次反复之前要进行初始化。随后，它会进行条件测试，而且在每一次反复的时候，进行某种形式的“步进”（Stepping）。for循环的形式如下：</p>

  <p>for(初始表达式; 布尔表达式; 步进)</p>

  <p>语句</p>

  <p>无论初始表达式，布尔表达式，还是步进，都可以置空。每次反复前，都要测试一下布尔表达式。若获得的结果是false，就会继续执行紧跟在for语句后面的那行代码。在每次循环的末尾，会计算一次步进。</p>

  <p>for循环通常用于执行“计数”任务：</p>

  <p>//: ListCharacters.java</p>

  <p>// Demonstrates "for" loop by listing</p>

  <p>// all the ASCII characters.</p>

  <p>public class ListCharacters {</p>

  <p>public static void main(String[] args) {</p>

  <p>for( char c = 0; c &lt; 128; c++)</p>

  <p>if (c != 26 ) // ANSI Clear screen</p>

  <p>System.out.println(</p>

  <p>"value: " + (int)c +</p>

  <p>" character: " + c);</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>注意变量c是在需要用到它的时候定义的――在for循环的控制表达式内部，而非在由起始花括号标记的代码块的最开头。c的作用域是由for控制的表达式。</p>

  <p>以于象C这样传统的程序化语言，要求所有变量都在一个块的开头定义。所以在编译器创建一个块的时候，它可以为那些变量分配空间。而在Java和C++中，则可在整个块的范围内分散变量声明，在真正需要的地方才加以定义。这样便可形成更自然的编码风格，也更易理解。</p>

  <p>可在for语句里定义多个变量，但它们必须具有同样的类型：</p>

  <p>for(int i = 0, j = 1;</p>

  <p>i &lt; 10 &amp;&amp; j != 11;</p>

  <p>i++, j++)</p>

  <p>/* body of for loop */;</p>

  <p>其中，for语句内的int定义同时覆盖了i和j。只有for循环才具备在控制表达式里定义变量的能力。对于其他任何条件或循环语句，都不可采用这种方法。</p>

  <p>1. 逗号运算符</p>

  <p>早在第1章，我们已提到了逗号运算符――注意不是逗号分隔符；后者用于分隔函数的不同自变量。Java里唯一用到逗号运算符的地方就是for循环的控制表达式。在控制表达式的初始化和步进控制部分，我们可使用一系列由逗号分隔的语句。而且那些语句均会独立执行。前面的例子已运用了这种能力，下面则是另一个例子：</p>

  <p>//: CommaOperator.java</p>

  <p>public class CommaOperator {</p>

  <p>public static void main(String[] args) {</p>

  <p>for(int i = 1, j = i + 10; i &lt; 5;</p>

  <p>i++, j = i * 2) {</p>

  <p>System.out.println("i= " + i + " j= " + j);</p>

  <p>}</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>输出如下：</p>

  <p>i= 1 j= 11</p>

  <p>i= 2 j= 4</p>

  <p>i= 3 j= 6</p>

  <p>i= 4 j= 8</p>

  <p>大家可以看到，无论在初始化还是在步进部分，语句都是顺序执行的。此外，尽管初始化部分可设置任意数量的定义，但都属于同一类型。</p>

  <p>3.2.6 中断和继续</p>

  <p>在任何循环语句的主体部分，亦可用break和continue控制循环的流程。其中，break用于强行退出循环，不执行循环中剩余的语句。而continue则停止执行当前的反复，然后退回循环起始和，开始新的反复。</p>

  <p>下面这个程序向大家展示了break和continue在for和while循环中的例子：</p>

  <p>//: BreakAndContinue.java</p>

  <p>// Demonstrates break and continue keywords</p>

  <p>public class BreakAndContinue {</p>

  <p>public static void main(String[] args) {</p>

  <p>for(int i = 0; i &lt; 100; i++) {</p>

  <p>if(i == 74) break; // Out of for loop</p>

  <p>if(i % 9 != 0) continue; // Next iteration</p>

  <p>System.out.println(i);</p>

  <p>}</p>

  <p>int i = 0;</p>

  <p>// An "infinite loop":</p>

  <p>while(true) {</p>

  <p>i++;</p>

  <p>int j = i * 27;</p>

  <p>if(j == 1269) break; // Out of loop</p>

  <p>if(i % 10 != 0) continue; // Top of loop</p>

  <p>System.out.println(i);</p>

  <p>}</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>在这个for循环中，i的值永远不会到达100。因为一旦i到达74，break语句就会中断循环。通常，只有在不知道中断条件何时满足时，才需象这样使用break。只要i不能被9整除，continue语句会使程序流程返回循环的最开头执行（所以使i值递增）。如果能够整除，则将值显示出来。</p>

  <p>第二部分向大家揭示了一个“无限循环”的情况。然而，循环内部有一个break语句，可中止循环。除此以外，大家还会看到continue移回循环顶部，同时不完成剩余的内容（所以只有在i值能被9整除时才打印出值）。输出结果如下：</p>

  <p>0</p>

  <p>9</p>

  <p>18</p>

  <p>27</p>

  <p>36</p>

  <p>45</p>

  <p>54</p>

  <p>63</p>

  <p>72</p>

  <p>10</p>

  <p>20</p>

  <p>30</p>

  <p>40</p>

  <p>之所以显示0，是由于0%9等于0。</p>

  <p>无限循环的第二种形式是for(;;)。编译器将while(true)与for(;;)看作同一回事。所以具体选用哪个取决于自己的编程习惯。</p>

  <p>1. 臭名昭著的“goto”</p>

  <p>goto关键字很早就在程序设计语言中出现。事实上，goto是汇编语言的程序控制结构的始祖：“若条件A，则跳到这里；否则跳到那里”。若阅读由几乎所有编译器生成的汇编代码，就会发现程序控制里包含了许多跳转。然而，goto是在源码的级别跳转的，所以招致了不好的声誉。若程序总是从一个地方跳到另一个地方，还有什么办法能识别代码的流程呢？随着Edsger Dijkstra著名的“Goto有害”论的问世，goto便从此失宠。</p>

  <p>事实上，真正的问题并不在于使用goto，而在于goto的滥用。而且在一些少见的情况下，goto是组织控制流程的最佳手段。</p>

  <p>尽管goto仍是Java的一个保留字，但并未在语言中得到正式使用；Java没有goto。然而，在break和continue这两个关键字的身上，我们仍然能看出一些goto的影子。它并不属于一次跳转，而是中断循环语句的一种方法。之所以把它们纳入goto问题中一起讨论，是由于它们使用了相同的机制：标签。</p>

  <p>“标签”是后面跟一个冒号的标识符，就象下面这样：</p>

  <p>label1:</p>

  <p>对Java来说，唯一用到标签的地方是在循环语句之前。进一步说，它实际需要紧靠在循环语句的前方――在标签和循环之间置入任何语句都是不明智的。而在循环之前设置标签的唯一理由是：我们希望在其中嵌套另一个循环或者一个开关。这是由于break和continue关键字通常只中断当前循环，但若随同标签使用，它们就会中断到存在标签的地方。如下所示：</p>

  <p>label1:</p>

  <p>外部循环{</p>

  <p>内部循环{</p>

  <p>//...</p>

  <p>break; //1</p>

  <p>//...</p>

  <p>continue; //2</p>

  <p>//...</p>

  <p>continue label1; //3</p>

  <p>//...</p>

  <p>break label1; //4</p>

  <p>}</p>

  <p>}</p>

  <p>在条件1中，break中断内部循环，并在外部循环结束。在条件2中，continue移回内部循环的起始处。但在条件3中，continue label1却同时中断内部循环以及外部循环，并移至label1处。随后，它实际是继续循环，但却从外部循环开始。在条件4中，break label1也会中断所有循环，并回到label1处，但并不重新进入循环。也就是说，它实际是完全中止了两个循环。</p>

  <p>下面是for循环的一个例子：</p>

  <p>//: LabeledFor.java</p>

  <p>// Java’s "labeled for loop"</p>

  <p>public class LabeledFor {</p>

  <p>public static void main(String[] args) {</p>

  <p>int i = 0;</p>

  <p>outer: // Can't have statements here</p>

  <p>for(; true ;) { // infinite loop</p>

  <p>inner: // Can't have statements here</p>

  <p>for(; i &lt; 10; i++) {</p>

  <p>prt("i = " + i);</p>

  <p>if(i == 2) {</p>

  <p>prt("continue");</p>

  <p>continue;</p>

  <p>}</p>

  <p>if(i == 3) {</p>

  <p>prt("break");</p>

  <p>i++; // Otherwise i never</p>

  <p>// gets incremented.</p>

  <p>break;</p>

  <p>}</p>

  <p>if(i == 7) {</p>

  <p>prt("continue outer");</p>

  <p>i++; // Otherwise i never</p>

  <p>// gets incremented.</p>

  <p>continue outer;</p>

  <p>}</p>

  <p>if(i == 8) {</p>

  <p>prt("break outer");</p>

  <p>break outer;</p>

  <p>}</p>

  <p>for(int k = 0; k &lt; 5; k++) {</p>

  <p>if(k == 3) {</p>

  <p>prt("continue inner");</p>

  <p>continue inner;</p>

  <p>}</p>

  <p>}</p>

  <p>}</p>

  <p>}</p>

  <p>// Can't break or continue</p>

  <p>// to labels here</p>

  <p>}</p>

  <p>static void prt(String s) {</p>

  <p>System.out.println(s);</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>这里用到了在其他例子中已经定义的prt()方法。</p>

  <p>注意break会中断for循环，而且在抵达for循环的末尾之前，递增表达式不会执行。由于break跳过了递增表达式，所以递增会在i==3的情况下直接执行。在i==7的情况下，continue outer语句也会到达循环顶部，而且也会跳过递增，所以它也是直接递增的。</p>

  <p>下面是输出结果：</p>

  <p>i = 0</p>

  <p>continue inner</p>

  <p>i = 1</p>

  <p>continue inner</p>

  <p>i = 2</p>

  <p>continue</p>

  <p>i = 3</p>

  <p>break</p>

  <p>i = 4</p>

  <p>continue inner</p>

  <p>i = 5</p>

  <p>continue inner</p>

  <p>i = 6</p>

  <p>continue inner</p>

  <p>i = 7</p>

  <p>continue outer</p>

  <p>i = 8</p>

  <p>break outer</p>

  <p>如果没有break outer语句，就没有办法在一个内部循环里找到出外部循环的路径。这是由于break本身只能中断最内层的循环（对于continue同样如此）。</p>

  <p>当然，若想在中断循环的同时退出方法，简单地用一个return即可。</p>

  <p>下面这个例子向大家展示了带标签的break以及continue语句在while循环中的用法：</p>

  <p>//: LabeledWhile.java</p>

  <p>// Java's "labeled while" loop</p>

  <p>public class LabeledWhile {</p>

  <p>public static void main(String[] args) {</p>

  <p>int i = 0;</p>

  <p>outer:</p>

  <p>while(true) {</p>

  <p>prt("Outer while loop");</p>

  <p>while(true) {</p>

  <p>i++;</p>

  <p>prt("i = " + i);</p>

  <p>if(i == 1) {</p>

  <p>prt("continue");</p>

  <p>continue;</p>

  <p>}</p>

  <p>if(i == 3) {</p>

  <p>prt("continue outer");</p>

  <p>continue outer;</p>

  <p>}</p>

  <p>if(i == 5) {</p>

  <p>prt("break");</p>

  <p>break;</p>

  <p>}</p>

  <p>if(i == 7) {</p>

  <p>prt("break outer");</p>

  <p>break outer;</p>

  <p>}</p>

  <p>}</p>

  <p>}</p>

  <p>}</p>

  <p>static void prt(String s) {</p>

  <p>System.out.println(s);</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>同样的规则亦适用于while：</p>

  <p>(1) 简单的一个continue会退回最内层循环的开头（顶部），并继续执行。</p>

  <p>(2) 带有标签的continue会到达标签的位置，并重新进入紧接在那个标签后面的循环。</p>

  <p>(3) break会中断当前循环，并移离当前标签的末尾。</p>

  <p>(4) 带标签的break会中断当前循环，并移离由那个标签指示的循环的末尾。</p>

  <p>这个方法的输出结果是一目了然的：</p>

  <p>Outer while loop</p>

  <p>i = 1</p>

  <p>continue</p>

  <p>i = 2</p>

  <p>i = 3</p>

  <p>continue outer</p>

  <p>Outer while loop</p>

  <p>i = 4</p>

  <p>i = 5</p>

  <p>break</p>

  <p>Outer while loop</p>

  <p>i = 6</p>

  <p>i = 7</p>

  <p>break outer</p>

  <p>大家要记住的重点是：在Java里唯一需要用到标签的地方就是拥有嵌套循环，而且想中断或继续多个嵌套级别的时候。</p>

  <p>在Dijkstra的“Goto有害”论中，他最反对的就是标签，而非goto。随着标签在一个程序里数量的增多，他发现产生错误的机会也越来越多。标签和goto使我们难于对程序作静态分析。这是由于它们在程序的执行流程中引入了许多“怪圈”。但幸运的是，Java标签不会造成这方面的问题，因为它们的活动场所已被限死，不可通过特别的方式到处传递程序的控制权。由此也引出了一个有趣的问题：通过限制语句的能力，反而能使一项语言特性更加有用。</p>

  <p>3.2.7 开关</p>

  <p>“开关”（Switch）有时也被划分为一种“选择语句”。根据一个整数表达式的值，switch语句可从一系列代码选出一段执行。它的格式如下：</p>

  <p>switch(整数选择因子) {</p>

  <p>case 整数值1 : 语句; break;</p>

  <p>case 整数值2 : 语句; break;</p>

  <p>case 整数值3 : 语句; break;</p>

  <p>case 整数值4 : 语句; break;</p>

  <p>case 整数值5 : 语句; break;</p>

  <p>//..</p>

  <p>default:语句;</p>

  <p>}</p>

  <p>其中，“整数选择因子”是一个特殊的表达式，能产生整数值。switch能将整数选择因子的结果与每个整数值比较。若发现相符的，就执行对应的语句（简单或复合语句）。若没有发现相符的，就执行default语句。</p>

  <p>在上面的定义中，大家会注意到每个case均以一个break结尾。这样可使执行流程跳转至switch主体的末尾。这是构建switch语句的一种传统方式，但break是可选的。若省略break，会继续执行后面的case语句的代码，直到遇到一个break为止。尽管通常不想出现这种情况，但对有经验的程序员来说，也许能够善加利用。注意最后的default语句没有break，因为执行流程已到了break的跳转目的地。当然，如果考虑到编程风格方面的原因，完全可以在default语句的末尾放置一个break，尽管它并没有任何实际的用处。</p>

  <p>switch语句是实现多路选择的一种易行方式（比如从一系列执行路径中挑选一个）。但它要求使用一个选择因子，并且必须是int或char那样的整数值。例如，假若将一个字串或者浮点数作为选择因子使用，那么它们在switch语句里是不会工作的。对于非整数类型，则必须使用一系列if语句。</p>

  <p>下面这个例子可随机生成字母，并判断它们是元音还是辅音字母：</p>

  <p>//: VowelsAndConsonants.java</p>

  <p>// Demonstrates the switch statement</p>

  <p>public class VowelsAndConsonants {</p>

  <p>public static void main(String[] args) {</p>

  <p>for(int i = 0; i &lt; 100; i++) {</p>

  <p>char c = (char)(Math.random() * 26 + 'a');</p>

  <p>System.out.print(c + ": ");</p>

  <p>switch(c) {</p>

  <p>case 'a':</p>

  <p>case 'e':</p>

  <p>case 'i':</p>

  <p>case 'o':</p>

  <p>case 'u':</p>

  <p>System.out.println("vowel");</p>

  <p>break;</p>

  <p>case 'y':</p>

  <p>case 'w':</p>

  <p>System.out.println(</p>

  <p>"Sometimes a vowel");</p>

  <p>break;</p>

  <p>default:</p>

  <p>System.out.println("consonant");</p>

  <p>}</p>

  <p>}</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>由于Math.random()会产生0到1之间的一个值，所以只需将其乘以想获得的最大随机数（对于英语字母，这个数字是26），再加上一个偏移量，得到最小的随机数。</p>

  <p>尽管我们在这儿表面上要处理的是字符，但switch语句实际使用的字符的整数值。在case语句中，用单引号封闭起来的字符也会产生整数值，以便我们进行比较。</p>

  <p>请注意case语句相互间是如何聚合在一起的，它们依次排列，为一部分特定的代码提供了多种匹配模式。也应注意将break语句置于一个特定case的末尾，否则控制流程会简单地下移，并继续判断下一个条件是否相符。</p>

  <p>1. 具体的计算</p>

  <p>应特别留意下面这个语句：</p>

  <p>char c = (char)(Math.random() * 26 + 'a');</p>

  <p>Math.random()会产生一个double值，所以26会转换成double类型，以便执行乘法运算。这个运算也会产生一个double值。这意味着为了执行加法，必须无将'a'转换成一个double。利用一个“造型”，double结果会转换回char。</p>

  <p>我们的第一个问题是，造型会对char作什么样的处理呢？换言之，假设一个值是29.7，我们把它造型成一个char，那么结果值到底是30还是29呢？答案可从下面这个例子中得到：</p>

  <p>//: CastingNumbers.java</p>

  <p>// What happens when you cast a float or double</p>

  <p>// to an integral value?</p>

  <p>public class CastingNumbers {</p>

  <p>public static void main(String[] args) {</p>

  <p>double</p>

  <p>above = 0.7,</p>

  <p>below = 0.4;</p>

  <p>System.out.println("above: " + above);</p>

  <p>System.out.println("below: " + below);</p>

  <p>System.out.println(</p>

  <p>"(int)above: " + (int)above);</p>

  <p>System.out.println(</p>

  <p>"(int)below: " + (int)below);</p>

  <p>System.out.println(</p>

  <p>"(char)('a' + above): " +</p>

  <p>(char)('a' + above));</p>

  <p>System.out.println(</p>

  <p>"(char)('a' + below): " +</p>

  <p>(char)('a' + below));</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>输出结果如下：</p>

  <p>above: 0.7</p>

  <p>below: 0.4</p>

  <p>(int)above: 0</p>

  <p>(int)below: 0</p>

  <p>(char)('a' + above): a</p>

  <p>(char)('a' + below): a</p>

  <p>所以答案就是：将一个float或double值造型成整数值后，总是将小数部分“砍掉”，不作任何进位处理。</p>

  <p>第二个问题与Math.random()有关。它会产生0和1之间的值，但是否包括值'1'呢？用正统的数学语言表达，它到底是(0,1)，[0,1]，(0,1]，还是[0,1)呢（方括号表示“包括”，圆括号表示“不包括”）？同样地，一个示范程序向我们揭示了答案：</p>

  <p>//: RandomBounds.java</p>

  <p>// Does Math.random() produce 0.0 and 1.0?</p>

  <p>public class RandomBounds {</p>

  <p>static void usage() {</p>

  <p>System.err.println("Usage: \n\t" +</p>

  <p>"RandomBounds lower\n\t" +</p>

  <p>"RandomBounds upper");</p>

  <p>System.exit(1);</p>

  <p>}</p>

  <p>public static void main(String[] args) {</p>

  <p>if(args.length != 1) usage();</p>

  <p>if(args[0].equals("lower")) {</p>

  <p>while(Math.random() != 0.0)</p>

  <p>; // Keep trying</p>

  <p>System.out.println("Produced 0.0!");</p>

  <p>}</p>

  <p>else if(args[0].equals("upper")) {</p>

  <p>while(Math.random() != 1.0)</p>

  <p>; // Keep trying</p>

  <p>System.out.println("Produced 1.0!");</p>

  <p>}</p>

  <p>else</p>

  <p>usage();</p>

  <p>}</p>

  <p>} ///:~</p>

  <p>为运行这个程序，只需在命令行键入下述命令即可：</p>

  <p>java RandomBounds lower</p>

  <p>或</p>

  <p>java RandomBounds upper</p>

  <p>在这两种情况下，我们都必须人工中断程序，所以会发现Math.random()“似乎”永远都不会产生0.0或1.0。但这只是一项实验而已。若想到0和1之间有2的128次方不同的双精度小数，所以如果全部产生这些数字，花费的时间会远远超过一个人的生命。当然，最后的结果是在Math.random()的输出中包括了0.0。或者用数字语言表达，输出值范围是[0,1)。</p>

  <p>3.3 总结</p>

  <p>本章总结了大多数程序设计语言都具有的基本特性：计算、运算符优先顺序、类型转换以及选择和循环等等。现在，我们作好了相应的准备，可继续向面向对象的程序设计领域迈进。在下一章里，我们将讨论对象的初始化与清除问题，再后面则讲述隐藏的基本实现方法。</p>

  <p>3.4 练习</p>

  <p>(1) 写一个程序，打印出1到100间的整数。</p>

  <p>(2) 修改练习(1)，在值为47时用一个break退出程序。亦可换成return试试。</p>

  <p>(3) 创建一个switch语句，为每一种case都显示一条消息。并将switch置入一个for循环里，令其尝试每一种case。在每个case后面都放置一个break，并对其进行测试。然后，删除break，看看会有什么情况出现。</p>

  <p>英文版主页 | 中文版主页 | 详细目录 | 关于译者</p>

  <p>-------------------------------------------------</p>

  <p>TXT书库 www.16txt.com</p>

  <p>-------------------------------------------------第4章 初</p>

  <div class="mbppagebreak"></div>
</body>
</html>
